/* vim:ft=objc:
 * $Id: Message.m 152 2005-11-28 01:53:29Z bmoore $
 *
 * Copyright (c) 2004, 2005  Branden J. Moore.
 *
 * This file is part of MacBiff, and is free software; you can redistribute
 * it and/or modify it under the terms of the GNU General Public License
 * version 2 as published by the Free Software Foundation.
 *
 * MacBiff is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with MacBiff; if not, write to the Free Software Foundation, Inc., 59
 * Temple Place, Suite 330, Boston, MA  02111-1307 USA.
 *
 */


#import "Message.h"
#import "PreferencesController.h"
#import "debug.h"

int b64translate( char bin )
{
	if ( bin >= 'A' && bin <= 'Z' )
		return bin-65;
	else if ( bin >= 'a' && bin <= 'z' )
		return ((bin-97) + 26);
	else if ( bin >= '0' && bin <= '9' )
		return ((bin-48) + 52);
	else if ( bin == '+' )
		return 62;
	else if ( bin == '/' )
		return 63;
	else
		return 0;
}

void deBase64(char* bin, char* bout)
{
	int val = 0;
	char *cval = (char*)&val;
	val = b64translate(bin[0]) << 18;
	val |= b64translate(bin[1]) << 12;
	val |= b64translate(bin[2]) << 6;
	val |= b64translate(bin[3]);

	bout[0] = cval[1];
	bout[1] = cval[2];
	bout[2] = cval[3];
}


NSString* deMIMEEncodedWord(NSString* instr)
{
	NSArray *parts = [instr componentsSeparatedByString: @"?"];
	if ( !parts || [parts count] != 5 ) {
		return [NSString stringWithString: instr];
	}

	NSString *charset = [parts objectAtIndex: 1];
	NSStringEncoding charEncode =
		CFStringConvertEncodingToNSStringEncoding(
				CFStringConvertIANACharSetNameToEncoding(
					(CFStringRef)charset));

	NSString *encoding = [parts objectAtIndex: 2];

	NSString *encodedText = [parts objectAtIndex: 3];
	NSMutableData *encodedData = [[[NSMutableData alloc] init] autorelease];
	[encodedData setData: [encodedText dataUsingEncoding:
				NSASCIIStringEncoding
			allowLossyConversion: YES]];
	int len = [encodedData length];

	if ([encoding caseInsensitiveCompare: @"Q"] == NSOrderedSame ) {
		char hex[3] = {0};
		int val = 0;
		char byte;
		int i;
		char *data = (char*)malloc(len);
		[encodedData getBytes: data];

		NSRange range;
		for ( i = len-3 ; i >= 0 ; --i ) {
			if ( data[i] != '=' ) {
				continue;
			} else {
				range = NSMakeRange(i, 3);
				hex[0] = data[i+1];
				hex[1] = data[i+2];
				sscanf(hex, "%x", &val);
				byte = val & 0xff;
				[encodedData replaceBytesInRange: range
					withBytes: (void*)&byte length: 1];
			}
		}
		free(data);
	} else if ([encoding caseInsensitiveCompare: @"B"] == NSOrderedSame ) {
		char fdata[4];
		char tdata[3];
		NSRange range;
		int i;
		for ( i = len-4 ; i >= 0 ; i-=4 ) {
			range = NSMakeRange(i, 4);
			[encodedData getBytes: fdata range: range];
			deBase64(fdata, tdata);
			[encodedData replaceBytesInRange: range
					withBytes: tdata length: 3];
		}
	}

	NSString *ostr= [[[NSString alloc] initWithData: encodedData
			encoding: charEncode] autorelease];
	return ostr;
}


NSString* deMIMEString( NSString *str )
{
	dprintf("deMIMEing string: '%s' [length: %d]\n", [str cString], [str length]);
	NSMutableString *nStr = [[[NSMutableString alloc] init] autorelease];
	[nStr setString: str];
	NSRange MIMERange = [nStr rangeOfString: @"=?"];
	while ( MIMERange.location != NSNotFound ) {
		NSRange MIMERange2 = [nStr rangeOfString: @"?"
				options: NSLiteralSearch
				range: NSMakeRange(MIMERange.location +2,
					[nStr length] - MIMERange.location -2)];
		MIMERange2 = [nStr rangeOfString: @"?="
				options: NSLiteralSearch
				range: NSMakeRange(MIMERange2.location+3,
					[nStr length] - MIMERange2.location -3)];
		if ( MIMERange2.location == NSNotFound ) {
			return nStr;
		}
		MIMERange.length = MIMERange2.location - MIMERange.location +2;
		NSString *pStr = deMIMEEncodedWord([nStr substringWithRange: MIMERange]);
		dprintf("deMIMEd component: '%s'\n", [pStr cString]);
		if ( pStr ) {
			/* Successful decoding */
			[nStr replaceCharactersInRange: MIMERange withString: pStr];
			MIMERange = [nStr rangeOfString: @"=?"];
		} else {
			/* Failed decoding.  Skip */
			MIMERange.location++;
			MIMERange.length = [nStr length] - MIMERange.location;
			MIMERange = [nStr rangeOfString: @"=?"
				options: NSLiteralSearch
				range: MIMERange];
		}
	}

	return nStr;
}


static unsigned gUid = 0;

@implementation Message

- (void) defaultAct: (id) obj
{
	[[NSWorkspace sharedWorkspace] launchApplication:
		[[prefs misc] objectForKey: @"Mail App"]];
}


+ (id) newMessageFrom: (NSString*) f
		email: (NSString*) e
	  withSubject: (NSString*) s
	       atDate: (NSDate*) d
	      withUid: (unsigned) u
{
	Message *m = [[Message alloc] initFrom: f
					 email: e
				   withSubject: s
					atDate: d
				       withUid: u];
	return [m autorelease];
}


+ (id) newMessageFrom: (NSString*) f
		email: (NSString*) e
	  withSubject: (NSString*) s
	       atDate: (NSDate*) d
{
	Message *m = [[Message alloc] initFrom: f
					 email: e
				   withSubject: s
					atDate: d];
	return [m autorelease];
}


- (id) initFrom: (NSString*) f
	  email: (NSString*) e
    withSubject: (NSString*) s
	 atDate: (NSDate*) d
	withUid: (unsigned) u
{
	if ( (self = [super init]) != Nil ) {
		date = [d copy];
		from = [deMIMEString(f) copy];
		email = [deMIMEString(e) copy];
		subject = [deMIMEString(s) copy];
		url = [@"" copy];
		unread = YES;
		new = YES;
		uid = u;
		NSString *tstr = [NSString stringWithFormat: @"%45@  %c  %@",
			 from, 0245, subject];
		menuItem = [[NSMenuItem alloc] initWithTitle: tstr
				action: @selector(defaultAct:)
				keyEquivalent: @""];
		[menuItem setTarget: self];
	}
	return self;
}


- (id) initFrom: (NSString*) f
	  email: (NSString*) e
    withSubject: (NSString*) s
	 atDate: (NSDate*) d
{
	return [self initFrom: f
			email: e
		  withSubject: s
		       atDate: d
		      withUid: gUid++];
}


- (void) dealloc
{
	if ( date) [date release];
	if ( from ) [from release];
	if ( subject) [subject release];
	if ( url ) [url release];

	[super dealloc];
}


- (NSDate*) date
{
	return date;
}


- (NSString*) from
{
	return from;
}


- (NSString*) email
{
	return email;
}


- (NSString*) subject
{
	return subject;
}


- (NSString*) url
{
	return url;
}


- (void) setURL: (NSString*) urlStr
{
	if ( url ) [url release];
	url = [urlStr copy];
}


- (BOOL) unread
{
	return unread;
}


- (void) setUnread: (BOOL) u
{
	unread = u;
}


- (BOOL) new
{
	return new;
}


- (void) setNew: (BOOL) n
{
	new = n;
}


- (unsigned) uid
{
	return uid;
}


- (void) setUID: (unsigned) u
{
	uid = u;
}


- (NSMenuItem*) menuItem
{
	return menuItem;
}


- (NSComparisonResult) compare: (Message*) anotherMessage
{
	NSComparisonResult res = [[self date] compare: [anotherMessage date]];
	if ( res == NSOrderedAscending )
		return NSOrderedDescending;
	else if ( res == NSOrderedDescending )
		return NSOrderedAscending;
	else
		return NSOrderedSame;
}


- (unsigned) hash
{
	return uid;
}


- (BOOL) isEqual: (id) anObject
{
	return ([self hash] == [anObject hash]);
}


- (id) copyWithZone: (NSZone*) zone
{
	Message *msg = (Message*)NSCopyObject(self, 0, zone);
	msg->date = [date copy];
	msg->from = [from copy];
	msg->email = [email copy];
	msg->subject = [subject copy];
	NSString *tstr = [NSString stringWithFormat: @"%45@  %c  %@", 
			 msg->from, 0245, msg->subject];
	msg->menuItem = [[NSMenuItem alloc] initWithTitle: tstr
				action: @selector(noOp:)
				keyEquivalent: @""];
	[msg->menuItem setTarget: msg];

	return msg;
}

@end
